﻿using System;
using System.Net;
using System.Net.Sockets;

namespace RQX.Common.Web.Tools
{
    public class SocketClient
    {
        #region 内部字段
        private string _ip;
        private int _port;
        private Socket _socket;
        /// <summary>
        /// 缓冲区最大容积
        /// </summary>
        private const int ContainerLength = 524288;
        #endregion

        #region 构造函数
        public SocketClient(int port) : this("127.0.0.1", port) { }
        public SocketClient(string ip, int port)
        {
            _ip = ip;
            _port = port;
        }
        #endregion

        #region 对外接口
        public byte[] SendAndGetRecv(byte[] buffer)
        {
            StartConnect();
            Send(buffer);
            return Recv();
        }
        #endregion

        #region 内部方法
        private void StartConnect()
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress address = IPAddress.Parse(_ip);
            IPEndPoint endPoint = new IPEndPoint(address, _port);
            TryCatchMethod(() =>
            {
                _socket.Connect(endPoint);
                HandleConnected?.Invoke(this);
            });
        }

        private void Close()
        {
            _socket.Close();
            HandleClosed?.Invoke(this);
        }

        private void Send(byte[] buffer)
        {
            TryCatchMethod(() =>
            {
                _socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
                HandleMsgSended?.Invoke(this);
            });
        }

        private byte[] Recv()
        {
            byte[] container = new byte[ContainerLength];
            try
            {
                var length = _socket.Receive(container, 0, container.Length, SocketFlags.None);
                byte[] buffer = new byte[length];
                Array.Copy(container, 0, buffer, 0, length);
                FuncMsgReceived?.Invoke(this, buffer);
                return buffer;
            }
            catch (Exception ex)
            {
                Close();
                HandleException?.Invoke(this, ex);
                return null;
            }
        }


        private void TryCatchMethod(Action action)
        {
            try
            {
                action();
            }
            catch (Exception ex)
            {
                Close();
                HandleException?.Invoke(this, ex);
            }
        }

        #endregion

        #region 事件处理
        /// <summary>
        /// 服务端接收到新连接后事件
        /// </summary>
        public Action<SocketClient> HandleConnected { get; set; }
        /// <summary>
        /// 接收到数据触发的事件
        /// </summary>
        public Action<SocketClient, byte[]> HandleMsgReceived { get; set; }
        /// <summary>
        /// 接收数据后返回
        /// </summary>
        public Func<SocketClient, byte[], string> FuncMsgReceived { get; set; }
        /// <summary>
        /// 发送数据后触发的事件
        /// </summary>
        public Action<SocketClient> HandleMsgSended { get; set; }
        /// <summary>
        /// 连接关闭事件
        /// </summary>
        public Action<SocketClient> HandleClosed { get; set; }
        /// <summary>
        /// 异常处理事件
        /// </summary>
        public Action<SocketClient, Exception> HandleException { get; set; }
        #endregion
    }
}
